﻿#region COPYRIGHT© 2009-2012 Phillip Clark. All rights reserved.

// For licensing information see License.txt (MIT style licensing).

#endregion

using System;
using System.Diagnostics.Contracts;
using FlitBit.Core.Parallel;

namespace FlitBit.Cache
{
	/// <summary>
	///   Local memory cache implementation that keeps only the most recently used
	///   items in memory and actively resolves missing items.
	/// </summary>
	/// <typeparam name="TKey">key type K</typeparam>
	/// <typeparam name="TValue">item type V</typeparam>
	public sealed class ActiveMruLocalMemoryCache<TKey, TValue> : MruLocalMemoryCache<TKey, TValue>
		where TValue : class
	{
		readonly bool _activeStats;
		readonly ICacheItemResolver<TKey, TValue> _resolver;

		/// <summary>
		///   Creates a new instance.
		/// </summary>
		/// <param name="name">the cache's name</param>
		/// <param name="resolver">a cache item resolver.</param>
		public ActiveMruLocalMemoryCache(string name, ICacheItemResolver<TKey, TValue> resolver)
			: this(name, null, resolver, null)
		{
			Contract.Requires<ArgumentNullException>(name != null);
			Contract.Requires<ArgumentException>(name.Length > 0);
			Contract.Requires<ArgumentNullException>(resolver != null);
		}

		/// <summary>
		///   Creates a new instance.
		/// </summary>
		/// <param name="name">the cache's name</param>
		/// <param name="resolver">
		///   a cache item resolver used to resolve missing
		///   items.
		/// </param>
		/// <param name="manager">a cache eviction manager</param>
		public ActiveMruLocalMemoryCache(string name, ICacheItemResolver<TKey, TValue> resolver, ICacheEvictionManager manager)
			: this(name, null, resolver, manager)
		{
			Contract.Requires<ArgumentNullException>(name != null);
			Contract.Requires<ArgumentException>(name.Length > 0);
			Contract.Requires<ArgumentNullException>(resolver != null);
		}

		/// <summary>
		///   Creates a new instance.
		/// </summary>
		/// <param name="name">the cache's name</param>
		/// <param name="options">options</param>
		/// <param name="resolver">
		///   a cache item resolver used to resolve missing
		///   items.
		/// </param>
		/// <param name="manager">a cache eviction manager</param>
		public ActiveMruLocalMemoryCache(string name,
			MruCacheOptions options,
			ICacheItemResolver<TKey, TValue> resolver,
			ICacheEvictionManager manager
			)
			: this(name, options, null, resolver, manager)
		{
			Contract.Requires<ArgumentNullException>(name != null);
			Contract.Requires<ArgumentException>(name.Length > 0);
			Contract.Requires<ArgumentNullException>(resolver != null);
		}

		/// <summary>
		///   Creates a new instance.
		/// </summary>
		/// <param name="name">the cache's name</param>
		/// <param name="options">options</param>
		/// <param name="stats">statistics collector</param>
		/// <param name="resolver">
		///   a cache item resolver used to resolve missing
		///   items.
		/// </param>
		/// <param name="manager">a cache eviction manager</param>
		public ActiveMruLocalMemoryCache(string name,
			MruCacheOptions options,
			IActiveCacheStatsCollector stats,
			ICacheItemResolver<TKey, TValue> resolver,
			ICacheEvictionManager manager
			)
			: base(name, options, stats, manager)
		{
			Contract.Requires<ArgumentNullException>(name != null);
			Contract.Requires<ArgumentException>(name.Length > 0);
			Contract.Requires<ArgumentNullException>(resolver != null);

			_resolver = resolver;
			_activeStats = this.Stats is IActiveCacheStatsCollector;
		}

		/// <summary>
		///   Creates a default instance of the cache stats collector for the instance.
		/// </summary>
		/// <returns>an instance</returns>
		protected override ICacheStatsCollector CreateDefaultStatsCollector()
		{
			return new BenignActiveCacheStatsCollector(Name);
		}

		/// <summary>
		///   Tries to resolve a missing item from the cache by delegating to the
		///   resolver.
		/// </summary>
		/// <param name="key">the item's key</param>
		/// <param name="continuation">a continuation called upon completion</param>
		protected override void TryResolveMissingItem(TKey key, Continuation<Tuple<ResolutionKinds, TKey, TValue>> continuation)
		{
			_resolver.TryResolve(key, continuation);
		}

		/// <summary>
		///   Updates statistics when items are resolved.
		/// </summary>
		/// <param name="stats">statistics collector</param>
		/// <param name="kind">kind of resolution</param>
		protected override void UpdateResolveCounters(ICacheStatsCollector stats, ResolutionKinds kind)
		{
			base.UpdateResolveCounters(stats, kind);
			if (_activeStats && kind.HasFlag(ResolutionKinds.Created))
			{
				((IActiveCacheStatsCollector) stats).IncrementResolves(1);
			}
		}
	}
}